#include "supergamer.hpp"

#include "protocol.hpp"

#include <iostream>

#include <math.h>

using namespace Proto;

namespace Client
{

const float SuperGamer::AIMING_FADE_OFF = 1;

SuperGamer::SuperGamer() :
	isAimed_(false), isCharging_(false), aimingTimeout_(1)
{
	for (short x = 0; x != FIELD_COLS; ++x)
		for (short y = 0; y != FIELD_ROWS; ++y)
			field_[x][y] = FIELD_NOT_VISIBLE;
}

void SuperGamer::load(const unsigned char *field, const Player &player)
{
	for (short y = 0; y != FIELD_ROWS; ++y)
		for (short x = 0; x != FIELD_COLS; ++x) {
			if (*field != FIELD_NOT_VISIBLE)
				field_[x][y] = *field;
			realField_[x][y] = *field;
			++field;
		}	
	player_ = player;
	isCharging_ = realField_[player.x][player.y] == FIELD_CHARGE_STATION &&
					player.energy != player.maxEnergy;
}

void SuperGamer::command(C2S::SpeedAdjust &sa, C2S::Fire &fire)
{
	if (player_.velocityModulo() == 0)
		isAimed_ = false;
	if (!aimingTimeout_--)
		isAimed_ = false;

	// select an aim
	if (!isAimed_)
		selectNewAim_();

	if (!isCharging_) {
		// compute desired velocity
		vx = 0;
		vy = 0;
		float value, fadeOff;
		
		for (short x = 0; x != FIELD_COLS; ++x)
			for (short y = 0; y != FIELD_ROWS; ++y)
				if (x != player_.x || y != player_.y) {
					valueObject_(field_[x][y], value, fadeOff);
					addPoint_(x, y, value, fadeOff);
				}

		addPoint_(aimx_, aimy_, AIMING_VALUE, AIMING_FADE_OFF);

		if (abs(vx) + abs(vy) > 1) {
		//if (abs(vx) + abs(vy) > player_.getMaxVelocity()) {
			// normalize desired velocity
			short maxVelocity = player_.getMaxVelocity();
			if (player_.energy == 0)
				maxVelocity = 1;
			float factor = maxVelocity/(abs(vx) + abs(vy));
			vx *= factor;
			vy *= factor;
		}

		float dvx = vx - player_.velx, dvy = vy - player_.vely;
		short xa = abs(dvx) > 0.5 ? sign_(dvx) : 0;
		short ya = abs(dvy) > 0.5 ? sign_(dvy) : 0;
		// compute speed adjust
		// if full braking is better than speed adjusting
		if (abs(vx) + abs(vy) + 1 < abs(vx - player_.velx - xa) + abs(vy - player_.vely -ya)) {
			sa.isBraking = true;
		}
		else {

			if (vectorModulo(player_.velx + xa, player_.vely + ya) <= player_.getMaxVelocity()) {
				sa.xAdjust = xa;
				sa.yAdjust = ya;
			}
			
		}
	}
	else {
		sa.isBraking = true;
	}
	
	fire_(sa, fire);
}

void SuperGamer::addPoint_(short x, short y, float value, float fadeOff)
{
	float dist = vectorModulo(player_.x - x, player_.y - y);
	float factor = value/pow(dist, fadeOff + 1);

	vx += (x - player_.x)*factor;
	vy += (y - player_.y)*factor;
}

void SuperGamer::selectNewAim_()
{
	aimx_ = rand() % FIELD_COLS;
	aimy_ = rand() % FIELD_ROWS;
	if (field_[aimx_][aimy_] == FIELD_NOT_VISIBLE) {
		// find the closest visible point
		short x, y;
		short x1 = player_.x, y1 = player_.y;
		for (x = 0; x != FIELD_COLS; ++x)
			for (y = 0; y != FIELD_ROWS; ++y)
				if (field_[x][y] == FIELD_EMPTY || field_[x][y] == FIELD_BONUS) {
					if (vectorModulo(aimx_ - x, aimy_ - y) <
						vectorModulo(aimx_ - x1, aimy_ - y1)) {
						x1 = x;
						y1 = y;						
					}
				}
		aimx_ = x1;
		aimy_ = y1;
	}
	isAimed_ = true;
	aimingTimeout_ = AIMING_TIMEOUT;
}

void SuperGamer::valueObject_(unsigned char objectId, float &value, float &fadeOff)
{
	// default values
	value = 0;
	fadeOff = 1;
	
	switch (objectId) {
		case FIELD_EMPTY:
			break;
			
		case FIELD_FOREST:
			value = -15;
			fadeOff = 3;
			break;

		case FIELD_SWAMP:
			value = -4;
			fadeOff = 3;
			break;

		case FIELD_RIVER:
			value =  -40;
			fadeOff = 3;
			break;

		case FIELD_BONUS:
			value =  10.0;
			break;

		case FIELD_ENEMY:
			if (player_.energy > 0) {
				value =  10;
				fadeOff = 0.5;
				break;
			}
			else {
				// could not fire
				break;
			}
		case FIELD_CHARGE_STATION:
			if (player_.energy < player_.maxEnergy/10) {
				value =  30;
				fadeOff = 0.5;
			}
			else
				break;
		default:
			break;
	}
}

void SuperGamer::fire_(C2S::SpeedAdjust &sa, C2S::Fire &fire)
{
	bool isHaveTarget = false;
	
	if (player_.shootCooldown <= 1 && player_.energy > 0) {
		for (short x = 0; x != FIELD_COLS; ++x) {
			for (short y = 0; y != FIELD_ROWS; ++y) {
				if (realField_[x][y] == FIELD_ENEMY &&
					vectorModulo(x - player_.x, y - player_.y) <= player_.shootSector) {
					isHaveTarget = true;
					
					if (player_.isCouldShoot(x, y)) {
						fire.xCoord = x;
						fire.yCoord = y;
						fire.power = player_.maxFire;
						return;
					}
				}
			}
		}
		// have a target and could not fire in this point
		if (isHaveTarget)
			sa.isBraking = true;
	}
}

SuperGamer::~SuperGamer()
{
}

}
